﻿namespace Fiddler
{
    using System;
    using System.IO;
    using System.Reflection;
    using System.Security.Cryptography.X509Certificates;

    public class CertMaker
    {
        private static ICertificateProvider oCertProvider = LoadOverrideCertProvider();

        static CertMaker()
        {
            if (oCertProvider == null)
            {
                oCertProvider = new DefaultCertificateProvider();
            }
        }

        public static bool createRootCert()
        {
            return oCertProvider.CreateRootCertificate();
        }

        public static void DoDispose()
        {
            if (CertMaker.oCertProvider != null)
            {
                if (FiddlerApplication.Prefs.GetBoolPref("fiddler.CertMaker.CleanupServerCertsOnExit", false))
                {
                    removeFiddlerGeneratedCerts(false);
                }
                IDisposable oCertProvider = CertMaker.oCertProvider as IDisposable;
                if (oCertProvider != null)
                {
                    oCertProvider.Dispose();
                }
                CertMaker.oCertProvider = null;
            }
        }

        internal static bool exportRootToDesktop()
        {
            try
            {
                byte[] bytes = getRootCertBytes();
                if (bytes != null)
                {
                    File.WriteAllBytes(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\FiddlerRoot.cer", bytes);
                    return true;
                }
                FiddlerApplication.DoNotifyUser("The root certificate could not be located.", "Export Failed");
            }
            catch (Exception exception)
            {
                FiddlerApplication.ReportException(exception, "Certificate Export Failed");
                return false;
            }
            return false;
        }

        public static X509Certificate2 FindCert(string sHostname)
        {
            return oCertProvider.GetCertificateForHost(sHostname);
        }

        internal static byte[] getRootCertBytes()
        {
            X509Certificate2 rootCertificate = GetRootCertificate();
            if (rootCertificate == null)
            {
                return null;
            }
            return rootCertificate.Export(X509ContentType.Cert);
        }

        public static X509Certificate2 GetRootCertificate()
        {
            return oCertProvider.GetRootCertificate();
        }

        private static ICertificateProvider LoadOverrideCertProvider()
        {
            string stringPref = FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.assembly", CONFIG.GetPath("App") + "CertMaker.dll");
            if (File.Exists(stringPref))
            {
                Assembly assembly;
                try
                {
                    assembly = Assembly.LoadFrom(stringPref);
                    if (!Utilities.FiddlerMeetsVersionRequirement(assembly, "Certificate Makers"))
                    {
                        FiddlerApplication.Log.LogFormat("Assembly {0} did not specify a RequiredVersionAttribute. Aborting load of Certificate Generation module.", new object[] { assembly.CodeBase });
                        return null;
                    }
                }
                catch (Exception exception)
                {
                    FiddlerApplication.LogAddonException(exception, "Failed to load CertMaker " + stringPref);
                    return null;
                }
                foreach (Type type in assembly.GetExportedTypes())
                {
                    if ((!type.IsAbstract && type.IsPublic) && (type.IsClass && typeof(ICertificateProvider).IsAssignableFrom(type)))
                    {
                        try
                        {
                            return (ICertificateProvider) Activator.CreateInstance(type);
                        }
                        catch (Exception exception2)
                        {
                            FiddlerApplication.DoNotifyUser(string.Format("[Fiddler] Failure loading {0} CertMaker from {1}: {2}\n\n{3}\n\n{4}", new object[] { type.Name, assembly.CodeBase, exception2.Message, exception2.StackTrace, exception2.InnerException }), "Load Error");
                        }
                    }
                }
                FiddlerApplication.Log.LogFormat("Assembly {0} did not contain a recognized ICertificateProvider.", new object[] { assembly.CodeBase });
            }
            return null;
        }

        public static bool removeFiddlerGeneratedCerts()
        {
            return removeFiddlerGeneratedCerts(true);
        }

        public static bool removeFiddlerGeneratedCerts(bool bRemoveRoot)
        {
            if (oCertProvider is ICertificateProvider2)
            {
                return (oCertProvider as ICertificateProvider2).ClearCertificateCache(bRemoveRoot);
            }
            return oCertProvider.ClearCertificateCache();
        }

        public static bool rootCertExists()
        {
            try
            {
                X509Certificate2 rootCertificate = GetRootCertificate();
                return (null != rootCertificate);
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static bool rootCertIsMachineTrusted()
        {
            bool flag;
            bool flag2;
            oCertProvider.rootCertIsTrusted(out flag, out flag2);
            return flag2;
        }

        public static bool rootCertIsTrusted()
        {
            bool flag;
            bool flag2;
            return oCertProvider.rootCertIsTrusted(out flag, out flag2);
        }

        public static bool StoreCert(string sHost, X509Certificate2 oCert)
        {
            ICertificateProvider3 oCertProvider = CertMaker.oCertProvider as ICertificateProvider3;
            if (oCertProvider == null)
            {
                return false;
            }
            if (!oCert.HasPrivateKey)
            {
                throw new ArgumentException("The provided certificate MUST have a private key.", "oCert");
            }
            return oCertProvider.CacheCertificateForHost(sHost, oCert);
        }

        public static void StoreCert(string sHost, string sPFXFilename, string sPFXPassword)
        {
            X509Certificate2 oCert = new X509Certificate2(sPFXFilename, sPFXPassword);
            if (!StoreCert(sHost, oCert))
            {
                throw new InvalidOperationException("The current ICertificateProvider does not support storing custom certificates.");
            }
        }

        public static bool trustRootCert()
        {
            return oCertProvider.TrustRootCertificate();
        }
    }
}

